/*
 *  Copyright (C) 2010 Glauco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.transaction;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import org.balisunrise.application.Application;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.common.Criterion;
import org.balisunrise.common.Query;
import org.balisunrise.common.Bindable;
import org.balisunrise.common.BindingGroup;
import org.balisunrise.common.Criterionable;
import org.balisunrise.common.EntityFieldType;
import org.balisunrise.common.EntityHolder;
import org.balisunrise.common.FieldLayout;
import org.balisunrise.common.SelectionAction;
import org.balisunrise.common.abstracts.AbstractSelectionFieldLayout;
import org.balisunrise.common.defaults.DefaultEntityHolder;

/**
 *
 * @author Glauco
 */
public class SelectionPanelField extends AbstractSelectionFieldLayout
        implements org.balisunrise.common.SelectionPanelField{

    /**
     * Mapa de repositório de entidades.
     */
    private HashMap<String,EntityHolder> entityHolders;

    /**
     * criterion deste SelectionPanelField.
     */
    private Criterion criterion;

    /**
     * Nome predecessor do nome do campo para filtrar.
     */
    private String fieldPreName;

    public SelectionPanelField() {
        entityHolders = new HashMap<String, EntityHolder>();
    }

    public String getFieldPreName() {
        return fieldPreName;
    }

    public void setFieldPreName(String fieldPreName) {
        String old = this.fieldPreName;
        this.fieldPreName = fieldPreName;
        changeSupport.firePropertyChange("fieldPreName", old, fieldPreName);
    }

    /**
     * Adiciona um nome predecessor ao nome do field.
     * É utilizado para filtrar atravéz de campos encapsulados por entidades
     * de entidades.
     * Por Exemplo:
     * A Classe Estado tem uma referencia a Pais, então para filtrar os
     * estados pelo nome do pais usariamos "pais.nome" onde "nome" seria o
     * campo e "pais" seria o pré-name.
     *
     * @param prefix
     */
    public void addPrefix(String prefix) {
        if (fieldPreName == null || fieldPreName.isEmpty())
            setFieldPreName(prefix);
        else
            setFieldPreName(prefix + "." + fieldPreName);
    }

    /**
     * Set's the selectedEntity.<br>
     * Para cada entityHolders, se existir, é atualiza seus valores.
     *
     * @param selectedEntity the selectedEntity
     */
    @Override
    public void setEntity(Serializable entity) {
        super.setEntity(entity);

        // atualiza entityContainers
        for (String s : entityHolders.keySet()) {
            entityHolders.get(s).updateFromSource(entity, s);
        }
    }

    /**
     * Atribui a Ação de seleção de entidades.<br>
     * É adicionado a R.V. da ação ao último SelectionPanelField deste grupo.
     * 
     * @param selectionAction the SelectionAction.
     */
    @Override
    public void setSelectionAction(SelectionAction selectionAction) {
        
        super.setSelectionAction(selectionAction);

        // atribui o grupo desta ação de seleção de entidades.
        selectionAction.setSelectionFieldLayout(this);

        //TODO: adicionar a R.V. da SelectionAction ao SelectionPanelField
        
        //List<PanelField<JComponent>> pfs = getSelectionLayoutableFields();
        // adiciona a R.V. da ação no último FieldLayout
        //FieldLayout<JComponent>[] fls = pfs.get(pfs.size()-1).getFieldLayouts();
        //
        //fls[fls.length-1].getComponents().add(
        //        this.selectionAction.getVisualRepresentation());
    }

    /**
     * Cria um Binding e o adiciona ao BindingGroup.
     * Faz o vinculo entre a propriedade do source e a propriedade
     * selectedEntity deste grupo.<br>
     * Depois, para cada PanelField deste grupo é feito o vinculo deste
     * objeto e a propriedade selectedEntity.<br>
     * No caso de algum PanelField deste grupo ser um outro SelectionPanelField,
     * é criado um EntityHolder e feito o vinculo entre o SelectionPanelField e
     * o EntityHolder.
     *
     * @param bindingGroup grupo de Bindings
     * @param source entidade com propriedades (campos) para vincular.
     * @param property propriedade do source a ser vinculada.
     */
    public void bindComponent(BindingGroup bindingGroup, Object source, String property) {

        // Propriedade do source para bind deste objeto.
        String thisProperty = property;
        if (!(source instanceof EntityHolder))
            thisProperty += "." + fieldName;

        Application.get().getBindingBuilder().createBinding(bindingGroup,
                source, thisProperty, this, PROP_ENTITY,
                null, false, false);
        
        for (FieldLayout fieldLayout : layouts) {

            // se for uma seleção
            if(fieldLayout instanceof SelectionPanelField){
                SelectionPanelField spg = (SelectionPanelField) fieldLayout;
                // cria container
                EntityHolder ec = new DefaultEntityHolder();
                // bind no holder
                spg.bindComponent(bindingGroup, ec,
                        EntityHolder.PROP_ENTITY);
                // adiciona o holder ao mapa.
                entityHolders.put(fieldLayout.getFieldName(), ec);
            } else if(fieldLayout instanceof Bindable){
                Bindable b = (Bindable) fieldLayout;
                b.bindComponent(bindingGroup,this,PROP_ENTITY);
            }
        }
    }

    public Query getQuery() {
        try{
            Query query = Application.get().getFactory().createBaliQuery(fieldClass);
            List<Criterionable> criterionables = extractCriterionables(this);
            for (Criterionable c : criterionables) {
                query.addCriterion(c.getCriterion());
            }
            return query;
        } catch (FactoryException fex){
            System.out.println(fex.getMessage());
            return null;
        }
    }

    public boolean isModified() {
        List<Criterionable> criterionables = extractCriterionables(this);
        for (Criterionable c : criterionables) {
            if(c.isModified())
                return true;
        }
        return false;
    }

    public Criterion getCriterion() {

        if (criterion == null)
            try{
                criterion = Application.get()
                        .getFactory()
                        .createBaliCriterion(fieldName);
                if(fieldPreName != null && fieldPreName.length() > 0){
                    if(fieldPreName.contains("."))
                        for (String s : fieldPreName.split(".")) {
                            criterion.addPreName(s);
                        }
                    else
                        criterion.addPreName(fieldPreName);
                }
            }catch (FactoryException fex){
                System.out.println("Falha ao criar BaliCriterion durante" +
                        " SelectionPanelGroup.getCriterion:\n" + fex.getMessage());
            }

        criterion.setFilterType(filterType);
        criterion.setEmptyForAll(emptyForAll);
        criterion.setValue(entity);

        return criterion;
    }

    public void clearComponentsValues() {
        for (org.balisunrise.common.FieldLayout fl : layouts) {
            fl.clearComponentsValues();
        }
    }

    public EntityFieldType getEntityFieldType() {
        return EntityFieldType.SELECTION_PANEL_FIELD;
    }
}
